package org.example.judge.oj;

import com.mysql.cj.log.Log;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.example.judge_common.hander.RetrunMsgException;
import org.example.judge_common.util.CompileUnit;
import org.example.judge_common.util.MyUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.*;
import java.lang.management.ManagementFactory;
import java.nio.charset.StandardCharsets;
import java.util.Scanner;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import com.sun.management.OperatingSystemMXBean;

@Data
@Slf4j
@NoArgsConstructor
public class GoJudge {

    private Long id = null;

    private Long newId = null;
    //是否使用中,true表示空闲中
    private boolean closeValue;

    // 是否已经编译过
    private boolean compiled;

    //上一次获取评测机的时间
    private long lastTime;
    public GoJudge(boolean b, CompileRun compileRun) {
        this.closeValue = b;
        this.compileRun = compileRun;
        this.compiled = false;
    }

    public void close() {
//        if(newId.equals(id)){
            this.id = null; //设置id
            this.closeValue = true;   //释放
            this.compiled = false;
//        }else{
//            log.warn("评测机重复释放");
//        }

    }


    private CompileRun compileRun;

    /**
     * @param input       测试数据输入
     * @param output      答案结果
     * @param code        源代码
     * @param timeLimit   最大运行时间
     * @param memoryLimit 最大内存
     * @param compileUnit 编译器
     * @return
     * @throws RetrunMsgException
     */
    public JudgeResult run(String input, String output, String code, Long timeLimit, Long memoryLimit, CompileUnit compileUnit) throws RetrunMsgException {

        JudgeResult runResult = this.privateRun(input, code, timeLimit, memoryLimit, compileUnit, null);

        if (runResult.getStatus() != JudgeResult.ACCEPTED) { //运行失败
            return runResult;
        }

        if (equalsOutput(output, runResult.getOutput())) {  //答案正确
            return runResult;
        } else {
            StringBuilder data = new StringBuilder();
            try {  //获取输出结果
                BufferedReader bufferedReader = new BufferedReader(new FileReader(runResult.getOutput()));
                String read;
                while (data.length() <= 150 && (read = bufferedReader.readLine()) != null)
                    data.append(read+"\n");
                bufferedReader.close();
                if (data.length() > 150) data.append("...");
            } catch (IOException e) {
                e.printStackTrace();
                return JudgeResult.WrongAnswer(runResult.getTime(), runResult.getMemory(), "结果输出失败");
            }
            return JudgeResult.WrongAnswer(runResult.getTime(), runResult.getMemory(), data.toString());
        }
    }

    /**
     * @param input       测试数据流输入
     * @param output      答案结果数据流
     * @param code        源代码
     * @param timeLimit   最大运行时间
     * @param memoryLimit 最大内存
     * @param compileUnit 编译器 默认编译一次
     * @return
     * @throws RetrunMsgException
     */
    public JudgeResult run(InputStream input, InputStream output, String code, Long timeLimit, Long memoryLimit, CompileUnit compileUnit) throws RetrunMsgException {
        JudgeResult judgeResult = this.privateRun(input, code, timeLimit, memoryLimit, compileUnit, null);
//        System.out.println(judgeResult);
        if (judgeResult.getStatus() != JudgeResult.ACCEPTED) { //运行失败
            return judgeResult;
        }
        if (equalsOutput(output, judgeResult.getOutput())) {  //答案正确
            return judgeResult;
        }
        return JudgeResult.WrongAnswer(judgeResult.getTime(), judgeResult.getMemory(), null);
    }


    /**
     * 不对运行结果进行对比
     *
     * @param input       测试数据(string || inputStream)
     * @param code        源代码
     * @param timeLimit   最大时间
     * @param memoryLimit 最大内存
     * @param compileUnit 编译器类型
     * @param isCompile   是否编译
     * @return
     * @throws JudgeResult
     */
    public JudgeResult privateRun(Object input, String code, Long timeLimit, Long memoryLimit, CompileUnit compileUnit, Boolean isCompile) throws RetrunMsgException {
        String errorString;
        if(isCompile==null)isCompile = !this.compiled;
        if (isCompile) {
            // 编译源码
            Process compile = compileRun.compile(code, compileUnit);
            errorString = this.getErrorString(compile);
            if (MyUtils.isNotEmpty(errorString)) { //编译失败
                return JudgeResult.CompileError(errorString);
            }
            // 判断是否存在编译后的文件
            if (!compileRun.isFile(compileUnit)) {
                return JudgeResult.CompileError("找不到编译后的执行文件");
            }
            this.compiled = true;
        }

        File file = null;
        //输入测试数据,强制类型转换为string或inputstream
        if (input instanceof String) file = compileRun.inputWriter((String) input);
        else file = compileRun.inputWriter((InputStream) input);
        //运行
        long beginTime = System.currentTimeMillis();
        OperatingSystemMXBean osmxb =  (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();

        long beginMemory = osmxb.getFreeMemorySize();
//        System.out.println(beginMemory);
        Process run = compileRun.run(file, compileUnit);
//        if(true)throw new RetrunMsgException("");
        long endTime = 0l;
        try {
            boolean b = run.waitFor(timeLimit, TimeUnit.MILLISECONDS);
            endTime = System.currentTimeMillis();
            if (!b) {//运行超时
                run.destroyForcibly();

                //杀除所有子进程
                run.descendants().forEach(new Consumer<ProcessHandle>() {
                    @Override
                    public void accept(ProcessHandle processHandle) {
                        try {
                            Process exec2 = Runtime.getRuntime().exec("cmd /c taskkill /pid " + processHandle.pid() + " -f");
                        } catch (IOException e) {
                            log.error("进程杀除失败，请手动删除Pid：{},错误信息：{}", processHandle.pid(), e.getMessage());
                        }
                    }
                });
                //返回运行超时
                return JudgeResult.TimeLimitExceeded(endTime - beginTime, 0);
            }

        } catch (InterruptedException e) {
            e.printStackTrace();
            return JudgeResult.RuntimeError("运行中断");
        }
        //判断是否报错
        errorString = this.getErrorString(run);
        if (MyUtils.isNotEmpty(errorString)) {//运行失败
            return JudgeResult.RuntimeError(errorString);
        }

        //保存结果到文件
        File out = new File(compileRun.getCodePath(), "1.out");
        // 直接输出到文件，不再转存
//        FileOutputStream fileOutputStream = null;
//        try {
//            fileOutputStream = new FileOutputStream(out);
//            if(compileUnit.getScale()!=CompileUnit.GCC.getScale())run.getInputStream().transferTo(fileOutputStream);
//            fileOutputStream.close();
//        } catch (IOException e) {
//            e.printStackTrace();

//            throw new RetrunMsgException("数据流输出到文件失败");
//        }
        return JudgeResult.Accepted(endTime - beginTime,  Math.abs((osmxb.getFreeMemorySize()-beginMemory)/1024), out);
    }

    /**
     * 对比两个答案 忽略格式问题
     *
     * @param data1 （string || InputStream）
     * @param data2 文件 file
     * @return
     */
    private boolean equalsOutput(Object data1, File data2) {
        //强制类型转换
        Scanner scanner1 = data1 instanceof String ? new Scanner((String) data1) : new Scanner((InputStream) data1);

        Scanner scanner2 = null;
        try {
            scanner2 = new Scanner(data2);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }


        while (scanner1.hasNext() || scanner2.hasNext()) {
            if (scanner1.hasNext() && scanner2.hasNext()) {
                if (!scanner1.next().equals(scanner2.next())) {
                    scanner1.close();
                    scanner2.close();
                    return false;  //答案错误
                }
            } else {
                scanner1.close();
                scanner2.close();
                return false;
            }

        }
        scanner1.close();
        scanner2.close();
        return true;
    }


    /**
     * 获取命令行运行结果的错误信息
     *
     * @param exec
     * @return
     * @throws IOException
     */
    private String getErrorString(Process exec) throws RetrunMsgException {
        if(exec==null)return null;
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new InputStreamReader(exec.getErrorStream(),"GBK"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        StringBuilder res = new StringBuilder();
        String s = null;
        try {
            while ((s = reader.readLine()) != null) {
                res.append(s);
            }
        } catch (IOException e) {
            e.printStackTrace();
            return "输出错误信息失败";
        }

        return res.toString();
    }


}
